{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 训练一个分类器\n",
    "\n",
    "训练流程如下：\n",
    "1. 采用 `torchvision` 加载和归一化 MNIST 训练集和测试集数据；\n",
    "2. 定义一个卷积神经网络\n",
    "3. 定义损失函数\n",
    "4. 在训练集上训练\n",
    "5. 在测试集上测试模型"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 1. 加载并归一化 MNIST"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "import torch.utils.data as Data\n",
    "import torchvision"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz\n",
      "Failed to download (trying next):\n",
      "HTTP Error 403: Forbidden\n",
      "\n",
      "Downloading https://ossci-datasets.s3.amazonaws.com/mnist/train-images-idx3-ubyte.gz\n",
      "Downloading https://ossci-datasets.s3.amazonaws.com/mnist/train-images-idx3-ubyte.gz to ../data/MNIST\\raw\\train-images-idx3-ubyte.gz\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 9912422/9912422 [00:20<00:00, 472155.46it/s] \n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Extracting ../data/MNIST\\raw\\train-images-idx3-ubyte.gz to ../data/MNIST\\raw\n",
      "\n",
      "Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz\n",
      "Failed to download (trying next):\n",
      "HTTP Error 403: Forbidden\n",
      "\n",
      "Downloading https://ossci-datasets.s3.amazonaws.com/mnist/train-labels-idx1-ubyte.gz\n",
      "Downloading https://ossci-datasets.s3.amazonaws.com/mnist/train-labels-idx1-ubyte.gz to ../data/MNIST\\raw\\train-labels-idx1-ubyte.gz\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 28881/28881 [00:00<00:00, 72332.90it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Extracting ../data/MNIST\\raw\\train-labels-idx1-ubyte.gz to ../data/MNIST\\raw\n",
      "\n",
      "Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz\n",
      "Failed to download (trying next):\n",
      "HTTP Error 403: Forbidden\n",
      "\n",
      "Downloading https://ossci-datasets.s3.amazonaws.com/mnist/t10k-images-idx3-ubyte.gz\n",
      "Downloading https://ossci-datasets.s3.amazonaws.com/mnist/t10k-images-idx3-ubyte.gz to ../data/MNIST\\raw\\t10k-images-idx3-ubyte.gz\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 1648877/1648877 [00:02<00:00, 784108.27it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Extracting ../data/MNIST\\raw\\t10k-images-idx3-ubyte.gz to ../data/MNIST\\raw\n",
      "\n",
      "Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz\n",
      "Failed to download (trying next):\n",
      "HTTP Error 403: Forbidden\n",
      "\n",
      "Downloading https://ossci-datasets.s3.amazonaws.com/mnist/t10k-labels-idx1-ubyte.gz\n",
      "Downloading https://ossci-datasets.s3.amazonaws.com/mnist/t10k-labels-idx1-ubyte.gz to ../data/MNIST\\raw\\t10k-labels-idx1-ubyte.gz\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 4542/4542 [00:00<00:00, 8304502.51it/s]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Extracting ../data/MNIST\\raw\\t10k-labels-idx1-ubyte.gz to ../data/MNIST\\raw\n",
      "\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "train_data = torchvision.datasets.MNIST(\n",
    "    root='../data/',\n",
    "    train=True,\n",
    "    transform=torchvision.transforms.ToTensor(),\n",
    "    download=True\n",
    ")\n",
    "train_loader = Data.DataLoader(dataset=train_data, batch_size=32, shuffle=True, num_workers=0)\n",
    "\n",
    "test_data = torchvision.datasets.MNIST(\n",
    "    root='../data/',\n",
    "    train=False,\n",
    "    transform=torchvision.transforms.ToTensor(),\n",
    ")\n",
    "test_loader = Data.DataLoader(dataset=test_data, batch_size=32, shuffle=False, num_workers=0)\n",
    "test_num = len(test_data)\n",
    "# print(' '.join('%5s' % classes[labels[j]] for j in range(4)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2. 定义一个卷积神经网络"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Net(\n",
      "  (conv1): Sequential(\n",
      "    (0): Conv2d(1, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
      "    (1): ReLU()\n",
      "    (2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
      "  )\n",
      "  (conv2): Sequential(\n",
      "    (0): Conv2d(16, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
      "    (1): ReLU()\n",
      "    (2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
      "  )\n",
      "  (fc): Linear(in_features=1568, out_features=10, bias=True)\n",
      ")\n"
     ]
    }
   ],
   "source": [
    "class Net(nn.Module):\n",
    "    def __init__(self):\n",
    "        super(Net, self).__init__()\n",
    "        self.conv1 = nn.Sequential(#(1, 28, 28)\n",
    "            nn.Conv2d(in_channels=1, out_channels=16, kernel_size=3, stride=1, padding=1),#(16, 28, 28)\n",
    "            nn.ReLU(),#(16, 28, 28)\n",
    "            nn.MaxPool2d(kernel_size=2)#(16, 14, 14)\n",
    "        )\n",
    "        self.conv2 = nn.Sequential(\n",
    "            nn.Conv2d(in_channels=16, out_channels=32, kernel_size=3, stride=1, padding=1),#(32, 14, 14)\n",
    "            nn.ReLU(),#(32, 14, 14)\n",
    "            nn.MaxPool2d(kernel_size=2)#(32, 7, 7)\n",
    "        )\n",
    "        self.fc = nn.Linear(32 * 7 * 7, 10)\n",
    "    def forward(self, x):\n",
    "        x = self.conv1(x)\n",
    "        x = self.conv2(x)\n",
    "        x = x.view(x.size(0), -1)\n",
    "        x = self.fc(x)\n",
    "        return x\n",
    "\n",
    "\n",
    "net = Net()\n",
    "print(net)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 3. 定义损失函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "cpu\n"
     ]
    }
   ],
   "source": [
    "device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n",
    "print(device)\n",
    "net.to(device)\n",
    "\n",
    "loss_fn = nn.CrossEntropyLoss()\n",
    "optim = torch.optim.Adam(net.parameters(), lr = 0.001)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 4. 训练网络"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "train loss:100%[**************************************************->]0.064047\n",
      "[epoch 1]train_loss:0.185  test_accuracy:0.980\n",
      "train loss:100%[**************************************************->]0.006857\n",
      "[epoch 2]train_loss:0.063  test_accuracy:0.986\n",
      "train loss:100%[**************************************************->]0.062165\n",
      "[epoch 3]train_loss:0.047  test_accuracy:0.986\n",
      "train loss:100%[**************************************************->]0.001546\n",
      "[epoch 4]train_loss:0.038  test_accuracy:0.987\n",
      "train loss:100%[**************************************************->]0.000872\n",
      "[epoch 5]train_loss:0.031  test_accuracy:0.988\n",
      "train loss:100%[**************************************************->]0.000738\n",
      "[epoch 6]train_loss:0.026  test_accuracy:0.989\n",
      "train loss:100%[**************************************************->]0.093841\n",
      "[epoch 7]train_loss:0.022  test_accuracy:0.989\n",
      "train loss:100%[**************************************************->]0.000442\n",
      "[epoch 8]train_loss:0.019  test_accuracy:0.988\n",
      "train loss:100%[**************************************************->]0.000235\n",
      "[epoch 9]train_loss:0.017  test_accuracy:0.989\n",
      "train loss:100%[**************************************************->]0.000487\n",
      "[epoch 10]train_loss:0.014  test_accuracy:0.987\n",
      "Finisher Training\n"
     ]
    }
   ],
   "source": [
    "save_path='./mnist.pth'\n",
    "best_acc=0.0\n",
    "for epoch in range(10):\n",
    "\n",
    "    \n",
    "    net.train()\n",
    "    running_loss=0.0\n",
    "    for step,data in enumerate(train_loader,start=0):\n",
    "        images,labels=data\n",
    "        images,labels=images.to(device),labels.to(device)\n",
    "        optim.zero_grad()\n",
    "        logits=net(images)\n",
    "        loss=loss_fn(logits,labels)\n",
    "        loss.backward()\n",
    "        optim.step()\n",
    "\n",
    "        running_loss+=loss.item()\n",
    "        rate=(step+1)/len(train_loader)\n",
    "        a=\"*\"*int(rate*50)\n",
    "        b=\".\"*int((1-rate)*50)\n",
    "        print(\"\\rtrain loss:{:^3.0f}%[{}->{}]{:4f}\".format(int(rate*100),a,b,loss),end=\"\")\n",
    "    print()\n",
    "\n",
    "    net.eval()\n",
    "    acc=0.0\n",
    "    with torch.no_grad():\n",
    "        for data_test in test_loader:\n",
    "            test_images,test_labels=data_test\n",
    "            test_images,test_labels=test_images.to(device),test_labels.to(device)\n",
    "            outputs=net(test_images)\n",
    "            predict_y=torch.max(outputs,dim=1)[1]\n",
    "\n",
    "            acc+=(predict_y==test_labels).sum().item()\n",
    "        test_accurate=acc/test_num\n",
    "        if test_accurate>best_acc:\n",
    "            best_acc=test_accurate\n",
    "            torch.save(net.state_dict(),save_path)\n",
    "        print('[epoch %d]train_loss:%.3f  test_accuracy:%.3f'%\n",
    "              (epoch+1,running_loss/step,test_accurate))\n",
    "        \n",
    "print(\"Finisher Training\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 5. 测试模型效果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([3.6924e-03, 5.9976e-04, 5.2746e-02, 2.4635e-02, 2.1107e-04, 2.8658e-03,\n",
      "        1.3966e-03, 1.9150e-03, 8.8301e-01, 2.8926e-02])\n",
      "8 0.8830122\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\86136\\AppData\\Local\\Temp\\ipykernel_21372\\3816252170.py:21: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.\n",
      "  model.load_state_dict(torch.load(model_weight_path))\n"
     ]
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from PIL import Image\n",
    "import matplotlib.pyplot as plt\n",
    "from torchvision import transforms\n",
    "import torch\n",
    "\n",
    "img=Image.open(\"./ps_mnist_8.png\")\n",
    "\n",
    "plt.imshow(img)\n",
    "\n",
    "train_transform=transforms.Compose([\n",
    "    transforms.Grayscale(),\n",
    "    transforms.Resize((28,28)),\n",
    "    transforms.ToTensor(),\n",
    "])\n",
    "\n",
    "img=train_transform(img)\n",
    "img=torch.unsqueeze(img,dim=0)\n",
    "\n",
    "model=Net()\n",
    "model_weight_path=\"./mnist.pth\"\n",
    "model.load_state_dict(torch.load(model_weight_path))\n",
    "\n",
    "index_to_class=['0','1','2','3','4','5','6','7','8','9']\n",
    "\n",
    "model.eval()\n",
    "with torch.no_grad():\n",
    "    y=model(img)\n",
    "    output=torch.squeeze(y)\n",
    "    predict=torch.softmax(output,dim=0)\n",
    "    print(predict)\n",
    "    predict_cla=torch.argmax(predict).numpy()\n",
    "print(index_to_class[predict_cla],predict[predict_cla].numpy())\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
